/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-09-17     tyustli   first version
 */

#include <rtconfig.h> 
#ifdef LCD_USING_TFT_ST77916_DMA

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

#define INST_CMD 0x02
#define DATA_CMD 0x32

#define LCD_RST_PIN GET_PIN(E, 15)  // 复位引脚
#define LCD_CS_PIN  GET_PIN(B,11)   // CS引脚

static struct rt_semaphore  trans_sem  = {0};
static struct rt_semaphore frame_sync = {0};
 

 // 需要包含HAL头文件
 #include "stm32h7xx_hal.h"

 extern OSPI_HandleTypeDef hospi1;

 /*@Tips: 这个函数，在rtt中，必须从board\CubeMX_Config\Src\main.c中copy过来，因为rtt不使用。
 如果用户要使用HAL库操作外设，必须copy过来。下面2种设置，似乎都可以运行，没有区别。*/
 static void MX_OCTOSPI1_Init(void)
 {
 
   /* USER CODE BEGIN OCTOSPI1_Init 0 */
 
   /* USER CODE END OCTOSPI1_Init 0 */
 
   OSPIM_CfgTypeDef sOspiManagerCfg = {0};
 
   /* USER CODE BEGIN OCTOSPI1_Init 1 */
 
   /* USER CODE END OCTOSPI1_Init 1 */
   /* OCTOSPI1 parameter configuration*/
   hospi1.Instance = OCTOSPI1;
   hospi1.Init.FifoThreshold = 1;
   hospi1.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
   hospi1.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON;
   hospi1.Init.DeviceSize = 23;
   hospi1.Init.ChipSelectHighTime = 1;
   hospi1.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
   hospi1.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
   hospi1.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;
   hospi1.Init.ClockPrescaler = 6;
   hospi1.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_HALFCYCLE;
   hospi1.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE;
   hospi1.Init.ChipSelectBoundary = 0;
   hospi1.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED;
   hospi1.Init.MaxTran = 0;
   hospi1.Init.Refresh = 0;
   if (HAL_OSPI_Init(&hospi1) != HAL_OK)
   {
     Error_Handler();
   }
   sOspiManagerCfg.ClkPort = 1;
   sOspiManagerCfg.NCSPort = 1;
   sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW;
   if (HAL_OSPIM_Config(&hospi1, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
   {
     Error_Handler();
   }
   /* USER CODE BEGIN OCTOSPI1_Init 2 */
 
   /* USER CODE END OCTOSPI1_Init 2 */
 
 }


 static int rt_qspi_lcd_init(void)
 {
    rt_pin_mode(LCD_RST_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(LCD_CS_PIN, PIN_MODE_OUTPUT);   

    // 复位序列
    rt_pin_write(LCD_RST_PIN, 0);  // 拉低复位
    rt_thread_mdelay(10);         // 保持100ms
    rt_pin_write(LCD_RST_PIN, 1);  // 释放复位
    rt_thread_mdelay(10);         // 等待复位完成

    rt_pin_write(LCD_CS_PIN, 1);

    MX_OCTOSPI1_Init();
 
     return RT_EOK;
 }

 /*@Tips: INIT_DEVICE_EXPORT宏要求函数类型的返回值是int, 否则会报warning*/
INIT_DEVICE_EXPORT(rt_qspi_lcd_init);


 /*@Tips: 单独设置Qspi的频率的寄存器配置。如何设置？可以直接去追HAL_QSPI_Init的代码，看HAL库底层是如何设置寄存器的，然后copy过来
 稍微修改一下即可。我就是这么做的，照葫芦画瓢。*/
void Set_Ospi_Prescaler(uint8_t prescaler)
{
    MODIFY_REG(hospi1.Instance->DCR2, OCTOSPI_DCR2_PRESCALER, ((prescaler - 1U) << OCTOSPI_DCR2_PRESCALER_Pos));   
}

 /*@Tips: 这个函数，在rtt中，必须从stm32h7xx_it.c中copy过来，因为rtt不使用。如果用户要使用HAL，必须copy过来*/
 void OCTOSPI1_IRQHandler(void)
 {
   rt_interrupt_enter();
   HAL_OSPI_IRQHandler(&hospi1);
   rt_interrupt_leave();
 }
 
  /*@Tips: 这个函数，在rtt中，必须从stm32h7xx_it.c中copy过来，因为rtt不使用。如果用户要使用HAL，必须copy过来*/
 void MDMA_IRQHandler(void)
 {
   /* Check the interrupt and clear flag */
   rt_interrupt_enter();
   HAL_MDMA_IRQHandler(hospi1.hmdma);
   rt_interrupt_leave();
 }
 
 /*@Tips: Txcplt适合于裸机编程, 实现同步。由于这里使用了rtt，因此用信号量代替了*/
 __IO uint8_t TxCplt=0; 
 void HAL_OSPI_TxCpltCallback(OSPI_HandleTypeDef *hospi)
 {
   TxCplt=1; 
   rt_sem_release(&trans_sem);
 }
 







void write_cs(int cs)
{
#ifdef BSP_OSPI_USING_SOFTCS
    rt_pin_write(LCD_CS_PIN, cs);
#endif
}


 void write_command(uint8_t cmd)
 {
     /* 使用HAL QSPI直接发送命令 */
     /* 需要实现的函数*/
     write_cs(0);
     HAL_OSPI_Command(&hospi1, &(OSPI_RegularCmdTypeDef){
        .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG,
        .FlashId = HAL_OSPI_FLASH_ID_1,
        .InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE,
        .InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS,
        .Instruction = INST_CMD,
        .AddressMode = HAL_OSPI_ADDRESS_1_LINE,
        .AddressSize = HAL_OSPI_ADDRESS_24_BITS,
        .Address = cmd << 8,
        .DataMode = HAL_OSPI_DATA_NONE,
        .SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD
    }, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
    write_cs(1);
 }

 void write_command_data(uint8_t cmd, uint8_t *pdata, uint32_t length)
 {
     /* 使用HAL QSPI发送命令+数据 */
     /* 需要实现的函数*/
     write_cs(0);     
     HAL_OSPI_Command(&hospi1, &(OSPI_RegularCmdTypeDef){
        .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG,
        .FlashId = HAL_OSPI_FLASH_ID_1,
        .InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE,
        .InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS,
        .Instruction = INST_CMD,
        .AddressMode = HAL_OSPI_ADDRESS_1_LINE,
        .AddressSize = HAL_OSPI_ADDRESS_24_BITS,
        .Address = cmd << 8,
        .DataMode = HAL_OSPI_DATA_1_LINE,
        .NbData = length,
        .SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD
    }, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
    HAL_OSPI_Transmit(&hospi1, pdata, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
    write_cs(1);
 }

 void write_command_pixels_small_packet(uint8_t cmd, uint8_t *pdata, uint32_t length)
 {
     /* 使用HAL QSPI发送命令(单线)+数据(四线) */
     /* 需要实现的函数*/ 
     write_cs(0);    
     HAL_OSPI_Command(&hospi1, &(OSPI_RegularCmdTypeDef){
        .OperationType = HAL_OSPI_OPTYPE_COMMON_CFG,
        .FlashId = HAL_OSPI_FLASH_ID_1,
        .InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE,
        .InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS,
        .Instruction = DATA_CMD,
        .AddressMode = HAL_OSPI_ADDRESS_1_LINE,
        .AddressSize = HAL_OSPI_ADDRESS_24_BITS,
        .Address = cmd << 8,
        .DataMode = HAL_OSPI_DATA_4_LINES,
        .NbData = length,
        .SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD
    }, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);

#if 0
    HAL_OSPI_Transmit(&hospi1, pdata, HAL_OSPI_TIMEOUT_DEFAULT_VALUE); /*@Tips:如果用Polling，使用这个函数*/
#else 
    SCB_CleanDCache_by_Addr((uint32_t *)pdata, length);
    HAL_OSPI_Transmit_DMA(&hospi1, pdata);
   //  while(HAL_OSPI_GetState(&hospi1) != HAL_OSPI_STATE_READY); /*@Tips:如果使用中断TxCplt, 那么这个可以不用*/
    rt_sem_take(&trans_sem, RT_WAITING_FOREVER); /*@Tips:由于是rtt，我们用信号量实现同步*/
#endif
    write_cs(1);
 }

 void write_command_pixels(uint8_t cmd, uint8_t *pdata, uint32_t length)
 {
     /* 拆分为多个传输包，因为dma一个包最大65536bytes */  
     uint32_t max_packet_size = 65536; // 最大单包大小
     uint32_t offset = 0;
     
     // 第一个包使用0x2c命令
     uint32_t first_packet_size = (length > max_packet_size) ? max_packet_size : length;
     write_command_pixels_small_packet(0x2c, pdata, first_packet_size);
     offset += first_packet_size;
     
     // 后续包使用0x3c命令
     while(offset < length) {
         uint32_t packet_size = (length - offset > max_packet_size) ? max_packet_size : (length - offset);
         write_command_pixels_small_packet(0x3c, pdata + offset, packet_size);
         offset += packet_size;
     }    
 }
 
 
 
 #define MAX_CMD_LEN 32
 static uint8_t lcd_init_cmds[][MAX_CMD_LEN] = {
     {0x01, 0},      /*soft reset*/
     {0x0, 1, 120},  /*这条伪指令表示延时120ms*/

     {0x3A, 1, 0x55},
     {0xF0, 1, 0x08},
     {0xF2, 1, 0x08},
     {0x9B, 1, 0x51},
     {0x86, 1, 0x53},
     {0xF2, 1, 0x80},
     {0xF0, 1, 0x00},
     {0xF0, 1, 0x01},
     {0xF1, 1, 0x01},
     {0xB0, 1, 0x54},
     {0xB1, 1, 0x3F},
     {0xB2, 1, 0x2A},
     {0xB4, 1, 0x46},
     {0xB5, 1, 0x34},
     {0xB6, 1, 0xD5},
     {0xB7, 1, 0x30},
     {0xBA, 1, 0x00},
     {0xBB, 1, 0x08},
     {0xBC, 1, 0x08},
     {0xBD, 1, 0x00},
     {0xC0, 1, 0x80},
     {0xC1, 1, 0x10},
     {0xC2, 1, 0x37},
     {0xC3, 1, 0x80},
     {0xC4, 1, 0x10},
     {0xC5, 1, 0x37},
     {0xC6, 1, 0xA9},
     {0xC7, 1, 0x41},
     {0xC8, 1, 0x51},
     {0xC9, 1, 0xA9},
     {0xCA, 1, 0x41},
     {0xCB, 1, 0x51},
     {0xD0, 1, 0x91},
     {0xD1, 1, 0x68},
     {0xD2, 1, 0x69},
     {0xF5, 2, 0x00, 0xA5},
     {0xDD, 1, 0x3F},
     {0xDE, 1, 0x3F},
     {0xF1, 1, 0x10},
     {0xF0, 1, 0x00},
     {0xF0, 1, 0x02},
     {0xE0, 14, 0xF0, 0x06, 0x0B, 0x09, 0x09, 0x16, 0x32, 0x44, 0x4A, 0x37, 0x13,
      0x13, 0x2E, 0x34},
     {0xE1, 14, 0xF0, 0x06, 0x0B, 0x09, 0x08, 0x05, 0x32, 0x33, 0x49, 0x17, 0x13,
      0x13, 0x2E, 0x34},
     {0xF0, 1, 0x10},
     {0xF3, 1, 0x10},
     {0xE0, 1, 0x0A},
     {0xE1, 1, 0x00},
     {0xE2, 1, 0x00},
     {0xE3, 1, 0x00},
     {0xE4, 1, 0xE0},
     {0xE5, 1, 0x06},
     {0xE6, 1, 0x21},
     {0xE7, 1, 0x00},
     {0xE8, 1, 0x05},
     {0xE9, 1, 0x82},
     {0xEA, 1, 0xDF},
     {0xEB, 1, 0x89},
     {0xEC, 1, 0x20},
     {0xED, 1, 0x14},
     {0xEE, 1, 0xFF},
     {0xEF, 1, 0x00},
     {0xF8, 1, 0xFF},
     {0xF9, 1, 0x00},
     {0xFA, 1, 0x00},
     {0xFB, 1, 0x30},
     {0xFC, 1, 0x00},
     {0xFD, 1, 0x00},
     {0xFE, 1, 0x00},
     {0xFF, 1, 0x00},
     {0x60, 1, 0x42},
     {0x61, 1, 0xE0},
     {0x62, 1, 0x40},
     {0x63, 1, 0x40},
     {0x64, 1, 0x02},
     {0x65, 1, 0x00},
     {0x66, 1, 0x40},
     {0x67, 1, 0x03},
     {0x68, 1, 0x00},
     {0x69, 1, 0x00},
     {0x6A, 1, 0x00},
     {0x6B, 1, 0x00},
     {0x70, 1, 0x42},
     {0x71, 1, 0xE0},
     {0x72, 1, 0x40},
     {0x73, 1, 0x40},
     {0x74, 1, 0x02},
     {0x75, 1, 0x00},
     {0x76, 1, 0x40},
     {0x77, 1, 0x03},
     {0x78, 1, 0x00},
     {0x79, 1, 0x00},
     {0x7A, 1, 0x00},
     {0x7B, 1, 0x00},
     {0x80, 1, 0x48},
     {0x81, 1, 0x00},
     {0x82, 1, 0x05},
     {0x83, 1, 0x02},
     {0x84, 1, 0xDD},
     {0x85, 1, 0x00},
     {0x86, 1, 0x00},
     {0x87, 1, 0x00},
     {0x88, 1, 0x48},
     {0x89, 1, 0x00},
     {0x8A, 1, 0x07},
     {0x8B, 1, 0x02},
     {0x8C, 1, 0xDF},
     {0x8D, 1, 0x00},
     {0x8E, 1, 0x00},
     {0x8F, 1, 0x00},
     {0x90, 1, 0x48},
     {0x91, 1, 0x00},
     {0x92, 1, 0x09},
     {0x93, 1, 0x02},
     {0x94, 1, 0xE1},
     {0x95, 1, 0x00},
     {0x96, 1, 0x00},
     {0x97, 1, 0x00},
     {0x98, 1, 0x48},
     {0x99, 1, 0x00},
     {0x9A, 1, 0x0B},
     {0x9B, 1, 0x02},
     {0x9C, 1, 0xE3},
     {0x9D, 1, 0x00},
     {0x9E, 1, 0x00},
     {0x9F, 1, 0x00},
     {0xA0, 1, 0x48},
     {0xA1, 1, 0x00},
     {0xA2, 1, 0x04},
     {0xA3, 1, 0x02},
     {0xA4, 1, 0xDC},
     {0xA5, 1, 0x00},
     {0xA6, 1, 0x00},
     {0xA7, 1, 0x00},
     {0xA8, 1, 0x48},
     {0xA9, 1, 0x00},
     {0xAA, 1, 0x06},
     {0xAB, 1, 0x02},
     {0xAC, 1, 0xDE},
     {0xAD, 1, 0x00},
     {0xAE, 1, 0x00},
     {0xAF, 1, 0x00},
     {0xB0, 1, 0x48},
     {0xB1, 1, 0x00},
     {0xB2, 1, 0x08},
     {0xB3, 1, 0x02},
     {0xB4, 1, 0xE0},
     {0xB5, 1, 0x00},
     {0xB6, 1, 0x00},
     {0xB7, 1, 0x00},
     {0xB8, 1, 0x48},
     {0xB9, 1, 0x00},
     {0xBA, 1, 0x0A},
     {0xBB, 1, 0x02},
     {0xBC, 1, 0xE2},
     {0xBD, 1, 0x00},
     {0xBE, 1, 0x00},
     {0xBF, 1, 0x00},
     {0xC0, 1, 0x12},
     {0xC1, 1, 0xAA},
     {0xC2, 1, 0x65},
     {0xC3, 1, 0x74},
     {0xC4, 1, 0x47},
     {0xC5, 1, 0x56},
     {0xC6, 1, 0x00},
     {0xC7, 1, 0x88},
     {0xC8, 1, 0x99},
     {0xC9, 1, 0x33},
     {0xD0, 1, 0x21},
     {0xD1, 1, 0xAA},
     {0xD2, 1, 0x65},
     {0xD3, 1, 0x74},
     {0xD4, 1, 0x47},
     {0xD5, 1, 0x56},
     {0xD6, 1, 0x00},
     {0xD7, 1, 0x88},
     {0xD8, 1, 0x99},
     {0xD9, 1, 0x33},
     {0xF3, 1, 0x01},
     {0xF0, 1, 0x00},
     {0xF0, 1, 0x01},
     {0xF1, 1, 0x01},
     {0xA0, 1, 0x0B},
     {0xA3, 1, 0x2A},
     {0xA5, 1, 0xC3},
     {0xA3, 1, 0x2B},
     {0xA5, 1, 0xC3},
     {0xA3, 1, 0x2C},
     {0xA5, 1, 0xC3},
     {0xA3, 1, 0x2D},
     {0xA5, 1, 0xC3},
     {0xA3, 1, 0x2E},
     {0xA5, 1, 0xC3},
     {0xA3, 1, 0x2F},
     {0xA5, 1, 0xC3},
     {0xA3, 1, 0x30},
     {0xA5, 1, 0xC3},
     {0xA3, 1, 0x31},
     {0xA5, 1, 0xC3},
     {0xA3, 1, 0x32},
     {0xA5, 1, 0xC3},
     {0xA3, 1, 0x33},
     {0xA5, 1, 0xC3},
     {0xA0, 1, 0x09},
     {0xF1, 1, 0x10},
     {0xF0, 1, 0x00},
     {0x2A, 4, 0x00, 0x00, 0x01, 0x67},
     {0x2B, 4, 0x00, 0x00, 0x01, 0x68},
     {0x4D, 1, 0x00},
     {0x4E, 1, 0x00},
     {0x4F, 1, 0x00},
     {0x4C, 1, 0x01},
     {0x4C, 1, 0x00},
     {0x2A, 4, 0x00, 0x00, 0x01, 0x67},
     {0x2B, 4, 0x00, 0x00, 0x01, 0x67},
     {0x21, 0},
 
     {0x11, 0},
     {0x0, 1, 120},  /*这条伪指令表示延时120ms*/
     {0x29, 0}, 
     
     // {0x36, 1, 0x20},   //0x20=旋转90度，0x00=不旋转
 };
 
 static int lcd_init()
 {
    /*@Tips: 初始化用低速*/
    Set_Ospi_Prescaler(32);    
     for (int i = 0; i < sizeof(lcd_init_cmds) / sizeof(lcd_init_cmds[0]); i++) {
         if (lcd_init_cmds[i][0] == 0x0) {
             rt_thread_mdelay(lcd_init_cmds[i][2]);
         }
         else if (lcd_init_cmds[i][1] == 0) {
             write_command(lcd_init_cmds[i][0]);
         }
         else {
             write_command_data(lcd_init_cmds[i][0], &lcd_init_cmds[i][2], lcd_init_cmds[i][1]);
         }
     }
      /*@Tips: 初始化结束后，恢复正常速度*/
      Set_Ospi_Prescaler(hospi1.Init.ClockPrescaler);    
 }
 
 INIT_COMPONENT_EXPORT(lcd_init);
 
 void lcd_fill_pixels(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint8_t *pcolor)
 {
     uint8_t xwin_list[]={(x0>>8)&0xff, x0&0xff, (x1>>8)&0xff, x1&0xff};
     uint8_t ywin_list[]={(y0>>8)&0xff, y0&0xff, (y1>>8)&0xff, y1&0xff};
     write_command_data(0x2a, xwin_list, 4);
     write_command_data(0x2b, ywin_list, 4);
 
     uint32_t length=2*(x1-x0+1)*(y1-y0+1);
     write_command_pixels(0x2c, (uint8_t *)pcolor, length);   
 }
 
 void lcd_fill_block(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t color)
 {
     uint8_t xwin_list[]={(x0>>8)&0xff, x0&0xff, (x1>>8)&0xff, x1&0xff};
     uint8_t ywin_list[]={(y0>>8)&0xff, y0&0xff, (y1>>8)&0xff, y1&0xff};
     write_command_data(0x2a, xwin_list, 4);
     write_command_data(0x2b, ywin_list, 4);
 
     uint32_t length=2*(x1-x0+1)*(y1-y0+1);
     uint8_t *pcolor=rt_malloc(length);
     for(int i=0;i<length;i+=2){
         pcolor[i]=color>>8;
         pcolor[i+1]=color&0xff;
     }
     write_command_pixels(0x2c, (uint8_t *)pcolor, length);
     rt_free(pcolor);
 }
 
 void lcd_clear(uint32_t color)
 {
     lcd_fill_block(0, 0, LCD_W - 1, LCD_H - 1, color);
 }
 
 static void lcd_clear_test()
 {
     lcd_clear(0xf800); rt_thread_mdelay(500);
     lcd_clear(0x07e0); rt_thread_mdelay(500);
     lcd_clear(0x001f); rt_thread_mdelay(500);   
 }
 
 MSH_CMD_EXPORT(lcd_clear_test, lcd_clear_test!)


 static int lcd_refresh_task_init(void)
 {
    rt_sem_init(&trans_sem, "lcdtrans", 0, RT_IPC_FLAG_PRIO); /*@Tips: 用于DMA操作的同步*/
    rt_sem_init(&frame_sync, "lcdsync", 0, RT_IPC_FLAG_PRIO); /*@Tips: 用于推屏和填充framebuffer的同步*/   
 }

 INIT_APP_EXPORT(lcd_refresh_task_init);
 
 #endif